package io.github.uicdb.et.block;

import io.github.uicdb.et.ModBlockEntityType;
import io.github.uicdb.et.ModElements;
import io.github.uicdb.et.capability.IElementalHandler;
import io.github.uicdb.et.capability.ModCapability;
import io.github.uicdb.et.capability.SimpleElementalHandler;
import io.github.uicdb.et.recipe.ExtractRecipe;
import io.github.uicdb.et.recipe.ElementRecipePart;
import io.netty.buffer.Unpooled;
import lombok.Getter;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.RecipeInput;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunk;
import org.checkerframework.common.value.qual.IntRange;
import org.jetbrains.annotations.NotNull;

@Getter

public class ExtractMachineBlockEntity extends BaseContainerBlockEntity implements ICraft {
    public static final int MAX_CONTAINER_SIZE = 400;
    NonNullList<ItemStack> stacks = NonNullList.withSize(1, ItemStack.EMPTY);
    final SimpleElementalHandler elementalHandler = new SimpleElementalHandler(MAX_CONTAINER_SIZE){
        @Override
        protected void onChange(ModElements elements, int before, int after) {
            ExtractMachineBlockEntity.this.setChanged();
        }
    };
    @Getter
    private final ElementalContainerData containerData = new ElementalContainerData(elementalHandler);

    public ExtractMachineBlockEntity(BlockPos pos, BlockState blockState) {
        super(ModBlockEntityType.EXTRACT_MACHINE.get(), pos, blockState);
    }

    public static void tick(Level level, BlockPos pos, BlockState state, ExtractMachineBlockEntity be) {
        if (level.getGameTime() % 4 != 0) return;
        var bbe = level.getBlockEntity(pos.below());
        if (bbe != null) {
            var cap = level.getCapability(ModCapability.ElementalHandler.BLOCK, pos.below(), Direction.UP);
            if (cap != null) {
                for (ModElements modElements : ModElements.values()) {
                    int am = cap.fillElemental(be.elementalHandler.drainElemental(1, modElements, IElementalHandler.ElementalAction.SIMULATE), modElements, IElementalHandler.ElementalAction.SIMULATE);
                    if (am > 0) {
                        cap.fillElemental(be.elementalHandler.drainElemental(am, modElements, IElementalHandler.ElementalAction.EXECUTE), modElements, IElementalHandler.ElementalAction.EXECUTE);
                    }
                }

            }
        }
    }

    public void tirggerCraft() {
        if (level == null) return;
        var itemStack = stacks.getFirst();
        if (itemStack.isEmpty()) return;
        var recipe = level.getRecipeManager().getAllRecipesFor(ExtractRecipe.TYPE).stream()
                .filter(r -> r.value().matches(new RecipeInput() {
                    @Override
                    public @NotNull ItemStack getItem(int i) {
                        return stacks.get(i);
                    }

                    @Override
                    public int size() {
                        return stacks.size();
                    }
                }, level))
                .findFirst();
        recipe.ifPresent(holder -> {
            var craftTimes = checkCanCraft(holder.value(), itemStack.getCount());//测试配方是否可以合成
            if (craftTimes <= 0) return;
            var r = holder.value();
            ItemStack remain = stacks.getFirst().getCraftingRemainingItem().copy();
            for (ElementRecipePart part : r.getResult()) {
                var modelement = ModElements.byHolder(part.element());
                if (modelement != null) {
                    if (elementalHandler.hasElemental(modelement)) {
                        elementalHandler.setElemental(modelement, Math.min(MAX_CONTAINER_SIZE, elementalHandler.getElemental(modelement) + part.amount() * craftTimes));
                    } else {
                        elementalHandler.setElemental(modelement, part.amount() * craftTimes);
                    }
                }
            }
            if (itemStack.getMaxStackSize() == 1)
                stacks.set(0, remain);
            else itemStack.shrink(craftTimes);
            setChanged();
        });
    }

    /**
     * 检测可以合成的数量
     *
     * @param recipe
     * @param amount
     * @return
     */
    private int checkCanCraft(ExtractRecipe recipe, int amount) {
        var parts = recipe.getResult();
        for (ElementRecipePart part : parts) {
            var modelement = ModElements.byHolder(part.element());
            var has = elementalHandler.getElemental(modelement);
            var remaining = MAX_CONTAINER_SIZE - has;
            var c = remaining / part.amount();
            return Math.min(amount, c);

        }
        return 0;
    }

    @Override
    protected void saveAdditional(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) {
        super.saveAdditional(tag, registries);
        ContainerHelper.saveAllItems(tag, stacks, true, registries);
        tag.put("container", elementalHandler.serializeNBT(registries));
    }

    @Override
    protected void loadAdditional(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) {
        super.loadAdditional(tag, registries);
        ContainerHelper.loadAllItems(tag, stacks, registries);
        elementalHandler.deserializeNBT(registries, tag.getCompound("container"));
    }

    @Override
    protected @NotNull Component getDefaultName() {
        return getBlockState().getBlock().getName();
    }


    @Override
    protected @NotNull NonNullList<ItemStack> getItems() {
        return this.stacks;
    }

    @Override
    protected void setItems(@NotNull NonNullList<ItemStack> nonNullList) {
        this.stacks = nonNullList;
        setChanged();
    }

    @Override
    protected @NotNull AbstractContainerMenu createMenu(int containerId, @NotNull Inventory inventory) {
        FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
        buf.writeBlockPos(getBlockPos());
        return new ExtractMenu(containerId, inventory, this, containerData, buf);
    }

    @Override
    public int getContainerSize() {
        return stacks.size();
    }


    @Override
    public void setChanged() {
        if (level != null && !level.isClientSide) {
            if (level.hasChunkAt(getBlockPos())) {
                LevelChunk levelChunk = level.getChunkAt(getBlockPos());
                levelChunk.setUnsaved(true);
            }
        }
    }

    @Override
    public @NotNull ItemStack getItem(int i) {
        return stacks.get(i);
    }


    public ModElements byIndex(@IntRange(from = 0, to = 6) int index) {
        return ModElements.values()[index];
    }

}
